#include <Utils/Type.h>
#include "HalAlgSSF33.h"
#include "HalAlgGloBuff.h"
#include "string.h"
#include "../alg_pr/ALGSsf33_PR.h"

#define SSF33_BLOCK_SIZE 	16
#define SSF33_KEY_LEN 		32
#define SSF33_IV_LEN		16

typedef struct {
    UINT8 buf[16];
    UINT32  count;
} SSF33_MAC_CONTEXT;

UINT32 HalAlgSSF33_Init(UINT8 Key[], UINT8 IV[])
{
	UINT8 *l_ptr;
	
	if(Key != NULL)
	{	       
		l_ptr = (UINT8*)GetAlgKeyBuff();
		memcpy(l_ptr, Key, SSF33_KEY_LEN);  
	}

	if(IV != NULL)
	{
		l_ptr =  (UINT8*)GetAlgIVBuff();
		memcpy(l_ptr, IV, SSF33_IV_LEN);
	}

	return RT_OK;
}

UINT32 HalAlgSSF33_Encrypt_Update(UINT32 Mode, UINT8 *pInBuf, UINT16 nSize, UINT8 *pOutBuf)
{
    UINT8 nRet;
    UINT8 l_mode;
    UINT8* l_iv_ptr, *l_key_ptr;

	if(pInBuf ==NULL)
	{
		return RT_OK;
	}

	if(nSize % SSF33_BLOCK_SIZE != 0)
	{
		return RT_FAIL;	
	}

	l_iv_ptr =(UINT8*)GetAlgIVBuff();
	l_key_ptr = (UINT8*)GetAlgKeyBuff();
	
	switch(Mode)
	{
		case HALALGSSF33_MODE_ECB:
			l_mode = SSF33_ECB;
			l_iv_ptr = NULL;
			break;
		case HALALGSSF33_MODE_CBC:
			l_mode = SSF33_CBC;			
			break;
		case HALALGSSF33_MODE_CFB:
		    l_mode = SSF33_CFB;
			break;
		case HALALGSSF33_MODE_OFB:
			l_mode = SSF33_OFB;
			break;
		default :
			return RT_FAIL;
	}
	 
    nRet = ALGSsf33_PR_Run(SSF33_ENCRYPT, l_mode, pInBuf, pOutBuf, nSize, l_key_ptr, l_iv_ptr);
   
	return nRet;
}

UINT32 HalAlgSSF33_Decrypt_Update(UINT32 Mode, UINT8 *pInBuf, UINT16 nSize, UINT8 *pOutBuf)
{
    UINT8 nRet ;
    UINT8 l_mode;
    UINT8* l_iv_ptr, *l_key_ptr;

	if(pInBuf ==NULL)
	{
		return RT_FAIL;
	}

	if(nSize % SSF33_BLOCK_SIZE != 0)
	{
		return RT_FAIL;	
	}
	
	l_iv_ptr =(UINT8*)GetAlgIVBuff();
	l_key_ptr = (UINT8*)GetAlgKeyBuff();
	
	switch(Mode)
	{
		case HALALGSSF33_MODE_ECB:
			l_mode = SSF33_ECB;
			l_iv_ptr = NULL;
			break;
		case HALALGSSF33_MODE_CBC:
			l_mode = SSF33_CBC;			
			break;
		case HALALGSSF33_MODE_CFB:
		    l_mode = SSF33_CFB;
			break;
		case HALALGSSF33_MODE_OFB:
			l_mode = SSF33_OFB;
			break;
		default :
			return RT_FAIL;
	} 
	
    nRet = ALGSsf33_PR_Run(SSF33_DECRYPT, l_mode, pInBuf, pOutBuf, nSize, l_key_ptr, l_iv_ptr);
   
	return nRet;
}

UINT32 HalAlgSSF33_Final(void)
{
	UINT8 *l_ptr;

	l_ptr = (UINT8*)GetAlgIVBuff();
	memset(l_ptr, 0, SSF33_IV_LEN);
	l_ptr = (UINT8*)GetAlgKeyBuff();
	memset(l_ptr, 0, SSF33_KEY_LEN);

	return RT_OK;
}

UINT32 HalAlgSSF33Mac_Init(UINT8 IV[], UINT8 Key[])
{
	SSF33_MAC_CONTEXT *pContext;	
    	
	pContext = (SSF33_MAC_CONTEXT*)GetAlgMacContextBuff();
	memset(pContext, 0, sizeof(SSF33_MAC_CONTEXT));

   	return (HalAlgSSF33_Init(Key, IV));
}

UINT32 HalAlgSSF33Mac_Update(UINT8 *pBuf, UINT16 nSize) 
{
	UINT8 *l_iv_ptr;
	UINT8 Iv[SSF33_IV_LEN] = {0};
	UINT16 ncount, i;
	SSF33_MAC_CONTEXT *pContext;	
    	
	l_iv_ptr = (UINT8*)GetAlgIVBuff();
	
	pContext = (SSF33_MAC_CONTEXT*)GetAlgMacContextBuff();
	
	ncount = pContext->count;
	
	memcpy(Iv, l_iv_ptr, SSF33_BLOCK_SIZE);
	
	while(nSize + ncount >= SSF33_BLOCK_SIZE)
	{
        memcpy(&pContext->buf[ncount], pBuf, SSF33_BLOCK_SIZE - ncount);
        
        for(i = 0; i < SSF33_BLOCK_SIZE; i++)
		{
			pContext->buf[i] ^= Iv[i];
		}
		
		HalAlgSSF33_Encrypt_Update(HALALGSSF33_MODE_ECB, pContext->buf, SSF33_BLOCK_SIZE, pContext->buf);
		
		nSize -= (SSF33_BLOCK_SIZE - ncount);
		pBuf += (SSF33_BLOCK_SIZE - ncount);
		ncount = 0;	
		pContext->count = 0; 

		memcpy(Iv, pContext->buf, SSF33_BLOCK_SIZE);
       	memcpy(l_iv_ptr, Iv, SSF33_BLOCK_SIZE);		
	}
	
	if(nSize)
	{
		memcpy(&pContext->buf[ncount], pBuf, nSize);
		pContext->count += nSize;
		nSize -= nSize;
	}
	
	return (0);
}

UINT32 HalAlgSSF33Mac_Final(UINT8 *pMac)
{
	UINT8* l_iv_ptr;
	UINT8 Iv[SSF33_IV_LEN] = {0};
	UINT16 ncount, i;
	SSF33_MAC_CONTEXT *pContext;	
    	
	l_iv_ptr =  (UINT8*)GetAlgIVBuff();
	
	pContext = (SSF33_MAC_CONTEXT*)GetAlgMacContextBuff();
	
	memcpy(Iv, l_iv_ptr, SSF33_BLOCK_SIZE);
	
	ncount = pContext->count;
	pContext->buf[ncount] = 0x80;
	memset(&pContext->buf[ncount + 1], 0, SSF33_BLOCK_SIZE - ncount - 1);
		
    for(i = 0; i < SSF33_BLOCK_SIZE; i++)
	{
		pContext->buf[i] ^= Iv[i];
	}

	HalAlgSSF33_Encrypt_Update(HALALGSSF33_MODE_ECB, pContext->buf, SSF33_BLOCK_SIZE, pContext->buf);
	
	memcpy(pMac,pContext->buf, sizeof(UINT32));
	
	HalAlgSSF33_Final();	
	
	return RT_OK;
}

